/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ssp.strategy.agent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import ssp.agent.QualityDemand;
import ssp.database.DatabaseManager;
import ssp.database.NetworkMap;
import ssp.event.DepartureEvent;
import ssp.event.EventQueue;
import ssp.net.NWcomponent;
import ssp.service.FrontendServer;
import ssp.service.FrontendServerManager;
import ssp.strategy.agent.util.PathTable;
import ssp.topology.Node;
import ssp.utility.LoggingManager;
import ssp.utility.MapValueAscending;
import ssp.utility.Pair;
import ssp.utility.PropertyManager;

/**
 * @deprecated 未完成
 * Agent_v1クラスのStrategyを実際に実現したクラス<br>
 * 全探索を行う
 * @author yamazaki
 */
public class Agent_v1_Strategy_v4 extends Agent_v1_Strategy{

    @Override
    public List<FrontendServer> searchFrontendServer(QualityDemand qualityDemand, NWcomponent belongingNWcomponent, int hopNum, int chargeNum) {
        List<Integer> newList = DatabaseManager.getINSTANCE().getServiceMap().getFrontendServierList(qualityDemand.getTypeID());
        List<FrontendServer> resultList = new ArrayList<>();
        for(int id : newList) {
            resultList.add(FrontendServerManager.getINSTANCE().getFrontendServer(id));
        }
        return resultList;
    }

    @Override
    public Pair<FrontendServer, List<NWcomponent>> searchPath(QualityDemand qualityDemand, NWcomponent belongingNWcomponent, FrontendServer frontendServer) {
        NetworkMap NWDB = DatabaseManager.getINSTANCE().getNetworkMap();
        Map<List<NWcomponent>, List<Double>> candidate = PathTable.getINSTANCE().getPath(belongingNWcomponent.getID(), ((NWcomponent)frontendServer.getConnectedNWcomponent()).getID());
        Map<List<NWcomponent>, Double> path = new HashMap<>();
        for(Entry<List<NWcomponent>, List<Double>> entry : candidate.entrySet()) {
            boolean flg = true;
            for(int i=0 ; i<qualityDemand.getNetworkQualityList().size() ; i++) {
                if(qualityDemand.getNetworkQualityList().get(i) < entry.getValue().get(i)) {
                    flg = false;
                    break;
                }
            }
            if(flg) {
                Set<Node> connectedNodes = new HashSet<>();
                NWcomponent prevNode = null;
                NWcomponent currentNode = null;
                double weight = 0;
                for(NWcomponent node : entry.getKey()) {
                    connectedNodes.clear();
                    if(prevNode == null) {
                        prevNode = node;
                    } else if(currentNode == null) {
                        currentNode = node;
                        connectedNodes.add(node);
                        connectedNodes.add(prevNode.getEndsOfInternalPath(node));
                        Pair<Integer, Set<Node>> key = new Pair(prevNode.getID(), connectedNodes);
                        if(NWDB.getAvailability(key) < (qualityDemand.getBandwidth() / NWDB.getBandwidthUnit(key)) + PropertyManager.getValueAsDouble("NetworkComponentMargin")) {
                            flg = false;
                            break;
                        }
                        weight += NWDB.getRecipAvailability(key);
                    } else {
                        connectedNodes.add(prevNode);
                        connectedNodes.add(node);
                        Pair<Integer, Set<Node>> key = new Pair(currentNode.getID(), connectedNodes);
                        if(NWDB.getAvailability(key) < (qualityDemand.getBandwidth() / NWDB.getBandwidthUnit(key)) + PropertyManager.getValueAsDouble("NetworkComponentMargin")) {
                            flg = false;
                            break;
                        }
                        weight += NWDB.getRecipAvailability(key);
                        prevNode = currentNode;
                        currentNode = node;
                    }
                }
                connectedNodes.clear();
                connectedNodes.add(prevNode);
                connectedNodes.add(currentNode.getEndsOfInternalPath(prevNode));
                Pair<Integer, Set<Node>> key = new Pair(currentNode.getID(), connectedNodes);
                if(NWDB.getAvailability(key) < (qualityDemand.getBandwidth() / NWDB.getBandwidthUnit(key)) + PropertyManager.getValueAsDouble("NetworkComponentMargin")) {
                    flg = false;
                }
                if(!flg) {
                    continue;
                }
                weight += NWDB.getRecipAvailability(key);
                path.put(entry.getKey(), weight);
            }
        }
        TreeMap<List<NWcomponent>, Double> result = new TreeMap<>(new MapValueAscending(path));
        result.putAll(path);
        return new Pair<>(frontendServer, result.firstKey());
    }

    @Override
    public boolean makeService(FrontendServer frontendServer, List<NWcomponent> nwComponents, QualityDemand demand) {
        
        //利用料金
        double charge = 0.0;
        
        //フロントエンドサーバに到着できなければfalse
        if(!frontendServer.arrival(frontendServer.getServiceEntry(demand.getTypeID()))) {
            return false;
        }
        
        charge += frontendServer.getServiceEntry(demand.getTypeID()).getCharge();
        
        //ネットワークコンポーネントへ到着できなければ解放してfalse
        NWcomponent prevNode = null;
        NWcomponent currentNode = null;
        NWcomponent nextNode = null;
        for(NWcomponent node : nwComponents) {
            if(prevNode == null) {
                prevNode = node;
            } else if(currentNode == null) {
                currentNode = node;
                charge += prevNode.getNWresourceManager().getCharge(node, prevNode.getEndsOfInternalPath(node), demand.getBandwidth(), demand.getServiceTimeLength());
                if(!prevNode.getNWresourceManager().reserveResources(node, prevNode.getEndsOfInternalPath(node), demand.getBandwidth())) {
                    return false;
                }
            } else {
                nextNode = node;
                if(!currentNode.getNWresourceManager().reserveResources(prevNode, nextNode, demand.getBandwidth())) {
                    
                    //到着できない場合はこれまでのリソースを解放
                    NWcomponent n_prevNode = null;
                    NWcomponent n_currentNode= null;
                    for(NWcomponent n_node : nwComponents) {
                        if(n_prevNode == null) {
                            n_prevNode = n_node;
                        } else if(n_currentNode == null) {
                            n_currentNode = n_node;
                        } else {
                            n_currentNode.getNWresourceManager().releaseResources(n_prevNode, n_node, demand.getBandwidth());
                            n_prevNode = n_currentNode;
                            n_currentNode = n_node;
                            if(nextNode.equals(n_node)) {
                                break;
                            }
                        }
                    }
                    return false;
                }
                charge += currentNode.getNWresourceManager().getCharge(prevNode, nextNode, demand.getBandwidth(), demand.getServiceTimeLength());
                
                prevNode = currentNode;
                currentNode = nextNode;
            }
        }
        
        //ログ用
        StringBuilder path = new StringBuilder(50);
        
        for(NWcomponent node : nwComponents) {
            path.append(node.getID());
            path.append(", ");
        }
        
//        LoggingManager.logging(Level.INFO, "FrontendServer: " + frontendServer.getID() + ", path: " + path + " charge: " + charge + " is reserved.", new Throwable());
        if(EventQueue.getINSTANCE().isIsLogging())
            LoggingManager.logging(Level.INFO, "FrontendServer: " + frontendServer.getID() + ", path: " + path + " charge: " + charge + " is reserved.", null);

        //予約できれば退去イベントをキューに追加してtrue
        EventQueue eventQueue = EventQueue.getINSTANCE();
        eventQueue.addEvent(new DepartureEvent(eventQueue.getCurrentTime() + demand.getServiceTimeLength(), frontendServer, nwComponents, demand));
        
        return true;
    }

    @Override
    public String toString() {
        throw new UnsupportedOperationException("Not supported yet.");
    }
    
}
